package com.gzit.store;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.gzit.utils.StringUtils;

import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * 数据层：封装对SQLite的操作 。对外公开以POJO的方式操作SQLite的数据接口。<br>
 * 主要设计原则是约定优于配置。 <br>
 * ---------------------<br>
 * 使用方法：<br>
 * 1.在Application初始化 如下： <br>
 * StoreManager.dbName("数据库的名称"); <br>
 * StoreManager.dbVersion("数据的版本号"); <br>
 * StoreManager.regStores(Account.DUMY, Config.DUMY); <br>
 * StoreManager.instance =new StoreManager(Application context); <br>
 * 2.创建JAVABEAN持久类。参看Store类。<br>
 * 3.外部调用 如下：<br>
 * 
 * <code>
 * StoreManager.insert(Store s);//增
 * StoreManager.delete(Store s);//删
 * StoreManager.findAll(Store s);//查
 * StoreManager.update(Store s);//改
 * </code>
 * 
 * <code>
 * 持久化javabean
 * PersistJavaBeanExample bean = new PersistJavaBeanExample();<br>
 * bean.setName("TEDDY");
 * bean.setAge(10);
 * bean.store();
 * </code>
 * 
 * 
 * @author teddy
 * 
 */

public class StoreManager {

	public static final String TAG = "StoreManager";

	// /////////////在ApplicationEx中注册该单例对象/////////
	public static StoreManager instance;
	// ///////////////////////////////////////////////////

	// ////////////////在系统初始化的时候需要注册下要创建的表////////////////////////
	static List<Store> regedStores = new ArrayList<Store>();
	public static String DB_NAME = "appdb";// 应用程序使用到的数据库名
	public static int DB_VERSION = 1; // 应用程序使用到的数据库版本

	// storeName -->StoreCols
	static HashMap<String, String[]> COLS_MAPPER = new HashMap<String, String[]>();

	public static String[] getCOLS(String storeName) {
		return COLS_MAPPER.get(storeName);
	}

	/**
	 * 在Application中可调用该方法，完成数据库对象的初始化
	 * 
	 * @param ctx
	 * @param stores
	 */
	public static void init(Context ctx, Store... stores) {
		instance = new StoreManager(ctx);
		regStores(stores);

	}

	public static void regStores(Store... stores) {
		for (Store s : stores) {
			regStore(s);
		}
	}

	public static void regStore(Store s) {
		COLS_MAPPER.put(s.getStoreName(), s.genCOLS());
		regedStores.add(s);

	}

	public static void dbName(String dbName) {
		DB_NAME = dbName;

	}

	public static void dbVersion(int version) {
		DB_VERSION = version;

	}

	public static SQLiteDatabase db() {
		return StoreManager.instance.db;
	}

	// 子类可以通过该方法执行建库之外的其它数据库初始化操作，比如插入初始化数据等
	protected static void initDB(Context context, DBOpenHelper dbHelper,
			SQLiteDatabase db) {

	}

	// ////////////////////////////////////////////////

	private SQLiteDatabase db;
	private final DBOpenHelper dbOpenHelper;

	public static class DBOpenHelper extends SQLiteOpenHelper {
		private Context context;

		public DBOpenHelper(final Context context, final String dbName,
				final int version) {
			super(context, StoreManager.DB_NAME, null, StoreManager.DB_VERSION);
			this.context = context;
		}

		@Override
		public void onCreate(final SQLiteDatabase db) {
			try {
				for (Store s : StoreManager.regedStores) {

					// 见表语句
					String ddl = s.getCreateSQL();
					Log.i(TAG, ddl);
					db.execSQL(ddl);

					// 在建表后执行的sql语句
					String postCreatSQL = s.getPostCreatSQL();

					if (!StringUtils.isBlank(postCreatSQL)) {
						Log.i(TAG, postCreatSQL);
						db.execSQL(postCreatSQL);

					}

				}

				// 在数据库创建的时候，进行的初始化工作
				initDB(context, this, db);

			} catch (SQLException e) {
				Log.e(TAG, "appdb create error:", e);
			}
		}

		// ////////内部的两个辅助方法//////////////////////////
		private void insert(Store store, final SQLiteDatabase db)
				throws Exception {
			store.rid = db
					.insert(store.getStoreName(), null, store.getValues());
		}

		public void insertAll(List<? extends Store> all, final SQLiteDatabase db)
				throws Exception {
			for (Store store : all) {
				insert(store, db);
			}

		}

		// /////////////////////

		public void onOpen(final SQLiteDatabase db) {
			super.onOpen(db);
		}

		@Override
		public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
				final int newVersion) {

			// NOTE:得慎重考虑如果升级时候是不是就真的只是简单地删除数据库
			for (Store s : StoreManager.regedStores) {
				db.execSQL("DROP TABLE IF EXISTS " + s.getStoreName());
			}
			this.onCreate(db);

		}
	}

	public StoreManager(final Context context) {
		this.dbOpenHelper = new DBOpenHelper(context, StoreManager.DB_NAME,
				StoreManager.DB_VERSION);
		this.establishDb();
	}

	private void establishDb() {
		if (this.db == null) {
			this.db = this.dbOpenHelper.getWritableDatabase();
		}

	}

	public void cleanup() { // #3
		if (this.db != null) {
			this.db.close();
			this.db = null;
		}
	}

	public SQLiteDatabase getDb() {
		return db;
	}

	// ////////////////////////////////////////////////////
	// ////////////////公开给各个持久对象使用的公共方法////////////////
	public static void insertAll(Store[] all) throws Exception {
		for (Store store : all) {
			insert(store);
		}

	}

	public static void insertAll(List<? extends Store> all) throws Exception {
		for (Store store : all) {
			insert(store);
		}

	}

	public static void insert(Store store) throws Exception {
		store.rid = instance.db.insert(store.getStoreName(), null,
				store.getValues());

	}

	public static void update(Store store) throws Exception {
		instance.db.update(store.getStoreName(), store.getValues(), "rid="
				+ store.rid, null);
	}

	public static void delete(Store store) throws Exception {
		instance.db.delete(store.getStoreName(), "rid=" + store.rid, null);
	}

	public static void deleteAll(Store store) throws Exception {
		instance.db.delete(store.getStoreName(), null, null);

	}

	public static void deleteByCondition(Store store, String conditionStr) {

		instance.db.delete(store.getStoreName(), conditionStr, null);

	}

	public static void execSQL(String sql, Object[] bindArgs) {
		instance.db.execSQL(sql, bindArgs);
	}

	// //////////////////////////////////////////////////////////
	// //////////////////////////查询方法////////////////////////////

	/**
	 * 复杂查询，通过types指定返回的类型 types的取值为Store中的Store.INT,Store.DOUBLE等，具体参看Store中的描述
	 * 
	 */
	public static <T extends Object> List<Map<String, T>> rawQuery(String sql,
			String[] selectionArgs, int[] types) {
		List<Map<String, T>> datas = new LinkedList<Map<String, T>>();
		Cursor c = null;
		try {
			c = instance.db.rawQuery(sql, selectionArgs);
			int numRows = c.getCount();
			c.moveToFirst();
			for (int i = 0; i < numRows; ++i) {
				Map<String, T> data = new HashMap<String, T>();
				for (int j = 0; j < c.getColumnCount(); j++) {
					if (types == null) {
						data.put(c.getColumnName(j), (T) c.getString(j));
					} else {
						data.put(c.getColumnName(j),
								(T) Store.getColFromCursor(c, j, types[j]));
					}

				}
				datas.add(data);
				c.moveToNext();
			}
		} finally {
			if (c != null && !c.isClosed()) {
				c.close();
			}
		}
		return datas;

	}

	// 根据SQL查询,此为通用的查询方法:注意：所有的结果数据都转化为String，因为cursor里面没有getObject方法，为了简化接口就全部用string替代
	public static List<Map<String, String>> rawQuery(String sql,
			String[] selectionArgs) {

		return rawQuery(sql, selectionArgs, null);

	}

	/**
	 * 查询记录数
	 * 
	 * @param sql
	 *            必须是能够返回整形的sql语句，否则会跑出异常
	 * @param selectionArgs
	 * @return
	 */
	public static int count(String sql, String[] selectionArgs) {

		Cursor c = null;
		try {
			c = instance.db.rawQuery(sql, selectionArgs);
			c.moveToFirst();
			return c.getInt(0);

		} finally {
			if (c != null && !c.isClosed()) {
				c.close();
			}
		}

	}

	/**
	 * 通过rid内对序列查询关联的唯一对象，查询不到则返回null
	 * 
	 * @param <T>
	 * @param s
	 * @param rid
	 * @return
	 * @throws Exception
	 */
	public static <T extends Store> T findUnique(T s, Long rid)
			throws Exception {
		return findUniqueByCondition(s, "rid=?",
				new String[] { String.valueOf(rid) });
	}

	/**
	 * 通过条件查询唯一值，查询数据的时候返回null
	 * 
	 * @param <T>
	 * @param s
	 * @param selections
	 * @param selectionArgs
	 * @return
	 * @throws Exception
	 */

	public static <T extends Store> T findUniqueByCondition(T s,
			String selections, String[] selectionArgs) throws Exception {
		List<T> dl = findByConditions(s, selections, selectionArgs);
		if (dl.isEmpty()) {
			return null;
		}
		return dl.get(0);

	}

	/**
	 * 查询单表中所有的数据
	 * 
	 * @param <T>
	 * @param store
	 * @return
	 * @throws Exception
	 */
	public static <T extends Store> List<T> findAll(T store) throws Exception {
		return findByConditions(store, null, null, null);
	}

	public static <T extends Store> List<T> findAll(T store, String orderBy)
			throws Exception {
		return findByConditions(store, null, null, orderBy);
	}

	/**
	 * 单表查询
	 * 
	 * @param <T>
	 * @param store
	 * @param selections
	 * @param selectionArgs
	 * @return
	 * @throws Exception
	 */
	public static <T extends Store> List<T> findByConditions(T store,
			String selections, String[] selectionArgs) throws Exception {

		return findByConditions(store, selections, selectionArgs, null);

	}

	/**
	 * 单表查询
	 * 
	 * @param <T>
	 * @param store
	 * @param selections
	 * @param selectionArgs
	 * @param orderBy
	 * @return
	 * @throws Exception
	 */
	public static <T extends Store> List<T> findByConditions(T store,
			String selections, String[] selectionArgs, String orderBy)
			throws Exception {

		Cursor c = null;
		try {
			c = instance.db.query(store.getStoreName(), store.getCOLS(),
					selections, selectionArgs, null, null, orderBy);

			return cursor2stores(store, c);
		} catch (SQLException e) {
			Log.v(TAG, store.getStoreName(), e);
			throw new Exception("Query data error,caused:" + e.getMessage());
		} finally {
			if (c != null && !c.isClosed()) {
				c.close();
			}
		}

	}

	/**
	 * 将 cursor转化为Store可持久对象的列表
	 * 
	 * @param <T>
	 * @param store
	 * @param c
	 * @return
	 * @throws Exception
	 */
	private static <T extends Store> List<T> cursor2stores(T store, Cursor c)
			throws Exception {
		LinkedList<T> ret = new LinkedList<T>();
		int numRows = c.getCount();
		c.moveToFirst();
		for (int i = 0; i < numRows; ++i) {
			T bean = (T) store.getClass().newInstance();
			bean.setValues(c);
			c.moveToNext();
			ret.add(bean);
		}

		return ret;

	}

}
